/*
This file is part of CanFestival, a library implementing CanOpen Stack.

Copyright (C): Edouard TISSERANT and Francis DUPIN

See COPYING file for copyrights details.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


/*
    Wrapper Layer for CANFestival to interact with IXXAT VCI V3.X.X Drivers (vcisdk.lib)
    Provides external references for win32 library see win32.c (CanFestival-3)

    http://www.ixxat.com/download_vci_v3_en.html

    Currently the VCI CAN driver for Windows supports the following IXXAT interfaces:

    PC-I 04/PCI
    iPC-I 320/PCI II
    iPC-I 165/PCI
    iPC-I XC16/PCI
    iPC-I XC16/PMC
    iPC-I XC16/PCIe
    USB-to-CAN compact
    USB-to-CAN II
    CAN-IB100/PCIe
    CAN-IB200/PCIe
    CAN-IB120/PCIe Mini
    CAN-IB130/PCIe 104
    CAN-IB230/PCIe 104
    CAN@net II/VCI
    CANblue II
    FR-IB100/PCIe (only in combination with VCI V2.20)
    tinCAN 161

*/


/*************************************************************************
**
**************************************************************************
**
**    File: can_ixxat_win32.c
**    Summary: Wrapper to encapsulate handling of VCI3
**    Include vcisdk.lib
**
**************************************************************************
**************************************************************************
**
**  Functions:  canOpen_driver
**              canReceive_driver
**              TimerProc1
**              canClose_driver
**              canSend_drive
**
**/

#include <stdio.h>
#include "can_driver.h"
#include "def.h"
#include "winuser.h"

//  Include a path to the following header files provided with VCI V3.X.X
//      ...sdk/Microsoft_VisualC/inc
#include "vcinpl.h"
#include "VCI3.h"
#include "vcitype.h"
#include "vciguid.h"
#include "vcierr.h"



/************************************************************************
**   bus status polling cycle  milliseconds
*************************************************************************/
#define STATUS_POLLING_CYCLE 1000

/*************************************************************************
**   function prototypes
*************************************************************************/
void CALLBACK TimerProc1(void* lpParametar, BOOL TimerOrWaitFired);
void Display_Error(HRESULT hResult);


/*************************************************************************
**   static variables
*************************************************************************/
static HANDLE hDevice;       // device handle
static LONG   lCtrlNo;       // controller number
static HANDLE hCanCtl;       // controller handle
static HANDLE hCanChn;       // channel handle
static LONG   lMustQuit = 0; // quit flag for the receive thread
static HANDLE hTimerHandle;  // timer handle

/*************************************************************************
**    datatypes
*************************************************************************/

struct sLook_up_table
{
    char baud_rate[20];
    UINT8 bt0;
    UINT8 bt1;
};

struct sInterface_lookup_table
{
    char board_num[10];
    UINT8 num;
};

/*************************************************************************
**
**    Function      : canOpen_driver
**
**    Description   : Initializes the Control and Message Channels
**    Parameters    : s_BOARD *board        - pointer to board information
**    Returnvalue   : (CAN_HANDLE)board     - handle for CAN controller
**
*************************************************************************/
CAN_HANDLE __stdcall canOpen_driver(s_BOARD* board)
{
    HANDLE        hEnumerator;     // enumerator handle
    VCIDEVICEINFO VCIDeviceInfo;   // device info
    HRESULT hResult;
    int index,  boardNum;
    BOOL bResult;
    struct sLook_up_table sBitRate_lookup[9] =
    {
        {"10K", 0x31, 0x1C},
        {"20K", 0x18, 0x1C},
        {"50K", 0x09, 0x1C},
        {"100K", 0x04, 0x1C},
        {"125K", 0x03, 0x1C},
        {"250K", 0x01, 0x1C},
        {"500K", 0x00, 0x1C},
        {"800K", 0x00, 0x16},
        {"1M", 0x00, 0x14}
    };
    struct sInterface_lookup_table sInterfaceList[4] =
    {
        {"vcan0", 0},
        {"vcan1", 1},
        {"vcan2", 2},
        {"vcan3", 3}
    };
    for (boardNum = 0 ; boardNum < 4; boardNum++)   // determine canline selected
    {
        if (strcmp(sInterfaceList[boardNum].board_num, board->busname) == 0)
        {
            break;
        }
    }
    for (index = 0; index < 10; ++index)       // determine baudrate
    {
        if (strcmp(sBitRate_lookup[index].baud_rate, board->baudrate) == 0)
        {
            break;
        }
    }
    if (index == 9)
    {
        MSG_ERR_DRV("IXXAT::open: The given baudrate %S is invalid.", baud_rate);
        return NULL ;
    }
    /*
    **  The following can be used when the client has multiple CAN interfaces to slect from, and does not wish to use the
    **  selection dialog box
    /*

    /*
    hResult= vciEnumDeviceOpen(&hEnumerator);
     // This loop will increment the index of the device list and returns the decription of the CAN line chose by user
    if (hResult== VCI_OK)
    {
        for (index1=0; index1 <= sInterfaceList[boardNum].num; index1++){
            hResult=vciEnumDeviceNext(hEnumerator, &VCIDeviceInfo);
        }
    }
    printf("Device Selected: %s %s\n",VCIDeviceInfo.Manufacturer, VCIDeviceInfo.Description);
    if (hResult== VCI_OK)
        hResult=vciEnumDeviceClose(hEnumerator);

    if (hResult== VCI_OK)
        hResult= vciDeviceOpen(&VCIDeviceInfo.VciObjectId, &hDevice);
    */
    /*
    **  Display Interface Selection Dialog Box
    */
    hResult = vciDeviceOpenDlg(0, &hDevice);
    /*
    **  Establish and activate the message Channel
    */
    if (hResult == VCI_OK)
    {
        hResult = canChannelOpen(hDevice, 0, TRUE, &hCanChn);
    }
    //  Select Rx fifo size, Rx threshold, Tx fifo size, Tx threshold
    if (hResult == VCI_OK)
    {
        hResult = canChannelInitialize(hCanChn, 1024, 1, 128, 1);
    }
    if (hResult == VCI_OK)
    {
        hResult = canChannelActivate(hCanChn, TRUE);
    }
    /*
    ** Establish and Activate the Contol Channel
    */
    if (hResult == VCI_OK)
    {
        hResult = canControlOpen(hDevice, 0, &hCanCtl);
    }
    //  Select 11 or 29 bit IDs, Select operating mode
    if (hResult == VCI_OK)
    {
        hResult = canControlInitialize(hCanCtl, CAN_OPMODE_STANDARD | CAN_OPMODE_ERRFRAME, sBitRate_lookup[index].bt0, sBitRate_lookup[index].bt1);
    }
    //  With VCI it is possible to filter IDs, See VCI V3 manual. The following will accept all IDs
    if (hResult == VCI_OK)
    {
        hResult = canControlSetAccFilter(hCanCtl, FALSE, CAN_ACC_CODE_ALL, CAN_ACC_MASK_ALL);
    }
    if (hResult == VCI_OK)
    {
        hResult = canControlStart(hCanCtl, TRUE);
    }
    if (hResult != VCI_OK)
    {
        Display_Error(hResult);
        return NULL;
    }
    /*
    **   Create timer to poll bus status
    */
    bResult = CreateTimerQueueTimer(
                  & hTimerHandle,
                  NULL,
                  TimerProc1,    // Callback function
                  NULL,
                  0,
                  STATUS_POLLING_CYCLE,
                  WT_EXECUTEINIOTHREAD
              );
    return (CAN_HANDLE)board;
}


/*************************************************************************
**
**    Function      : canReceive_driver
**
**    Description   : Transfers RX messages to Festival application
**    Parameters    : CAN_HANDLE inst       - handle for CAN controller
                      Message *m            - pointer to Message struct
**    Returnvalue   : hResult               -   VCI_OK if success
**
*************************************************************************/
UNS8 __stdcall canReceive_driver(CAN_HANDLE inst, Message* m)
{
    HRESULT hResult;
    CANMSG rCanMsg;
    //Read message from the receive buffer
    hResult = canChannelReadMessage(hCanChn, INFINITE, &rCanMsg);
    if (hResult != VCI_OK)
    {
        return 1;
    }
    m->cob_id =  rCanMsg.dwMsgId;
    m->len = rCanMsg.uMsgInfo.Bits.dlc;
    m->rtr = rCanMsg.uMsgInfo.Bits.rtr;
    if (m->rtr == NOT_A_REQUEST)
    {
        memcpy(m->data, rCanMsg.abData, m->len);
    }
    return (UNS8)hResult;
}

/*************************************************************************
**
**    Function      : canSend_driver
**
**    Description   : Transfers RX messages to Interface
**    Parameters    : CAN_HANDLE inst       - handle for CAN controller
**                    Message *m            - pointer to Message struct
**    Returnvalue   : hResult               -   VCI_OK if success
**
*************************************************************************/
UNS8 __stdcall canSend_driver(CAN_HANDLE inst, Message const* m)
{
    HRESULT hResult;
    CANMSG sCanMsg;
    sCanMsg.uMsgInfo.Bytes.bType  = CAN_MSGTYPE_DATA;
    sCanMsg.uMsgInfo.Bytes.bFlags = CAN_MAKE_MSGFLAGS(8, 0, 0, 0, 0);
    sCanMsg.uMsgInfo.Bits.srr     = 0;
    sCanMsg.dwTime   = 0;
    sCanMsg.dwMsgId  = m->cob_id ;
    memcpy(sCanMsg.abData, m->data, m->len);
    sCanMsg.uMsgInfo.Bits.dlc = m->len;
    sCanMsg.uMsgInfo.Bits.rtr = m->rtr;
    // write the CAN message into the transmit FIFO without waiting for transmission
    hResult = canChannelPostMessage(hCanChn, &sCanMsg);
    return (UNS8)hResult;
}




/*************************************************************************
**
**    Function      : canClose_driver
**
**    Description   : Close the message and control channel
**    Parameters    : CAN_HANDLE inst       - handle for CAN controller
**    Returnvalue   :
**
*************************************************************************/
int __stdcall canClose_driver(CAN_HANDLE inst)
{
    printf("CAN close \n");
    canControlReset(hCanCtl);
    canChannelClose(hCanChn);
    canControlClose(hCanCtl);
    vciDeviceClose(hDevice);
    DeleteTimerQueueTimer(NULL, hTimerHandle, NULL);
    return 1;
}


/*************************************************************************
**
**    Function      : canChangeBaudRate_driver
**
**    Description   : Changes the Baudrate of the interface (not supported)
**    Parameters    :
**    Returnvalue   :
**
*************************************************************************/
UNS8 __stdcall canChangeBaudRate_driver(CAN_HANDLE fd, char* baud)
{
    //printf("canChangeBaudRate not yet supported by this driver\n");
    return 0;
}


/*************************************************************************
**
**    Function      : TimerProc1
**
**    Description   :  Basic Polling of the  the canline status,
**                     print debug message for buffer overflow, and BUSOFF condition
**    Parameters    :  void* lpParametar, BOOL TimerOrWaitFired
**    Returnvalue   :  none
**
*************************************************************************/
void CALLBACK TimerProc1(void* lpParametar, BOOL TimerOrWaitFired)
{
    HRESULT hResult;
    CANLINESTATUS canStatus;
    /* Establish CAN controller status */
    hResult = canControlGetStatus(hCanCtl, &canStatus);
    if (hResult != VCI_OK)
    {
        printf("Error did not read CAN STATUS\n");
    }
    switch (canStatus.dwStatus)
    {
    case CAN_STATUS_OVRRUN:
        printf("Overrun of the recieve buffer\n");
        break;
    case CAN_STATUS_ERRLIM:
        printf("Overrun of the CAN controller error counter \n");
        break;
    case CAN_STATUS_BUSOFF:
        printf("CAN status: BUSOFF");
        break;
    case CAN_STATUS_ININIT:
        break;
    case (CAN_STATUS_BUSOFF)+(CAN_STATUS_ININIT):
        printf("CAN status: BUSOFF\n");
        /*
        ** Bus off recovery should come after a software reset, and 128*11 recessive bits.
        ** This can only happen when an Error-Active node sends an active error flag,
        ** and other nodes respond with Error Echo Flag
        */
        break;
    default:
        break;
    }
};


/*************************************************************************
**
**    Function      : Display_Error
**
**    Description   :  Display the CANline Error
**    Parameters    :  HANDLE hResult
**    Returnvalue   :  none
**
*************************************************************************/
void Display_Error(HRESULT hResult)
{
    char szError[VCI_MAX_ERRSTRLEN];
    if (hResult != NO_ERROR)
    {
        if (hResult == -1)
        {
            hResult = GetLastError();
        }
        szError[0] = 0;
        vciFormatError(hResult, szError, sizeof(szError));
        printf("Error Establishing CAN channel, Error Code: %s\n", szError);
    }
}